/*=================================================================================
 * Class Array: Set the array for different dimensions
 *				all elements should be stored in a contiguous chunk of memory
 *				( no vector<vector> ! )
 *=================================================================================*/

#ifndef Array_HH
#define Array_HH

/*---------------------------------------------------------------------------------
 * Standard C++ library headers
 *---------------------------------------------------------------------------------*/

#include <iostream>
#include <cmath>

template <typename Type>

class Array
{
private:
#pragma pack(1)
	int ni = 1, nj = 1, nk = 1, nl = 1;

#pragma pack()
	int *Ptr;
	Type *Data = nullptr;
	int Dim;

	/* Set the dimension of an array */
	template <typename... Args>
	void SetDim(int i, Args... args)
	{
		*Ptr = i;
		Ptr++;
		SetDim(args...);
	}
	void SetDim(int i)
	{
		*Ptr = i;
		Ptr = &ni;
	}

	/* Get the index for Array operation */
	template <typename... Args>
	int GetIndex(int i, Args... args)
	{
		int count = sizeof...(args);
		int index = 1;
		for (int j = 1; j <= count; j++)
			index *= (*(Ptr + j));
		Ptr++;
		return index * i + GetIndex(args...);
	}
	int GetIndex(int i)
	{
		return i;
	}

public:
	/* Constructor */
	Array(){};

	/* Array initialization */
	template <typename... Args>
	void Initial(Args... args)
	{
		Dim = sizeof...(args);
		Ptr = &ni;
		SetDim(args...);
		Data = new Type[ni * nj * nk * nl];
		Fill(0);
	}

	/* Array operation */
	template <typename... Args>
	Type &operator()(Args... args)
	{
		int index = GetIndex(args...);
		Ptr = &ni;
		if (index >= ni * nj * nk * nl)
			std::cout << "Out boundary" << __FILE__ << __LINE__ << std::endl;
		return Data[index];
	}

	/* Assign a value to the whole array */
	void Fill(Type value)
	{
		for (int i = 0; i < GetSize(); i++)
			Data[i] = value;
	}

	/* Summation function of the array */
	double Sum()
	{
		double sum = 0;
		for (int i = 0; i < GetSize(); i++)
			sum = sum + Data[i];
		return sum;
	}

	/* Get the Maximum value */
	double MaxValue()
	{
		double max = Data[0];
		for (int i = 1; i < GetSize(); i++)
		{
			if (Data[i] > max)
				max = Data[i];
		}
		return max;
	}

	/* Get the Minimum value */
	double MinValue()
	{
		double min = Data[0];
		for (int i = 1; i < GetSize(); i++)
		{
			if (Data[i] < min)
				min = Data[i];
		}
		return min;
	}

	/* Judge if data is nan */
	bool IsNan()
	{
		bool tag = false;
		for (int i = 0; i < GetSize(); i++)
			if (std::isnan(Data[i]))
			{
				tag = true;
			}
		return tag;
	}

	/* Get information */

	int GetSize() { return ni * nj * nk * nl; }
	int GetDim() { return Dim; }
	int GetNi() { return ni; }
	int GetNj() { return nj; }
	int GetNk() { return nk; }

	/* Destructor */

	~Array(){};
};

#endif
